#include "TripelManager.h"


sTripelData TripelManager::getTripelData(sTripelId tId) {
    sRow row = getRow(tId);
    if ((row.header.bytes[0] & M_1011_x4) != 0x90)
        throw ERR_NO_TRIPEL_ROW;

    sSubjectId subjectId = sSubjectId{ bytesToInt3(row.bytes), bytesToInt2(row.bytes + 3) };
    sPredicateId predicateId = sPredicateId{ bytesToInt3(row.bytes + 5), bytesToInt2(row.bytes + 8) };
    sSubjectId objectId = sSubjectId{ bytesToInt3(row.bytes + 10), bytesToInt2(row.bytes + 13) };
    sTripelData tripelData = sTripelData{ subjectId, predicateId, objectId };

    BufferManager::unpinBlock(tId.blockId);

    return tripelData;
}

void TripelManager::removeTripel(sTripelId tId) {
    Block* block = BufferManager::pinBlock(tId.blockId);
    sRow row = block->getRow(tId.rowId);
    if ((row.header.bytes[0] & M_1111_x4) != 0x90)
        throw - 1; //kein Tripel-offset
    row.header.bytes[0] = 0x00;
    BufferManager::unpinBlock(tId.blockId);
}

void TripelManager::removeTripel(sTripel tripel) {
    removeTripel(findTripel(tripel));
}

void TripelManager::updateTripel(sTripel oTripel, sTripel nTripel) {
    removeTripel(oTripel);
    blockId newid = newTripel(nTripel).blockId;
    BufferManager::unpinBlock(newid);
}

sTripel TripelManager::getTripel(sTripelId tId) {
    sTripelData tripelData = getTripelData(tId);
    string subject = SubjectManager::getSubject(tripelData.subjectId);
    string predicate = PredicateManager::getPredicate(tripelData.predicateId);
    string object = SubjectManager::getSubject(tripelData.objectId);
    return{ subject, predicate, object };
}

void TripelManager::freeTripel(sTripelId tId) {
    BufferManager::unpinBlock(tId.blockId);
}

sTripelId TripelManager::findTripel(sTripel tripel) {
    bool subjSet = !tripel.subject.empty();
    bool predSet = !tripel.predicate.empty();
    bool objSet = !tripel.object.empty();
    sSubjectId subjectId;
    sPredicateId predicateId;
    sSubjectId objectId;
    if (subjSet) {
        try {
            subjectId = SubjectManager::findSubject(tripel.subject);
        }
        catch (Error) {
            throw - 1; //subject existiert nicht
        }
    }
    if (predSet) {
        try {
            predicateId = PredicateManager::findPredicate(tripel.predicate);
        }
        catch (Error) {
            throw - 1; //predicate existiert nicht
        }
    }
    if (objSet) {
        try {
            objectId = SubjectManager::findSubject(tripel.object);
        }
        catch (Error) {
            throw - 1; //object existiert nicht
        }
    }
    blockId bId = 0;
    Block* block = NULL;
    while (bId < DiscManager::getDiscSize()) {
        rowId rId = 0;
        block = BufferManager::pinBlock(bId);
        if (block->isFree()) {
            BufferManager::unpinBlock(bId);
            block = NULL;
            bId++;
            break;
        }
        uShort rowCount = block->getRowCount();
        while (rId < rowCount) {
            sRow row = block->getRow(rId);
            byte a = row.header.bytes[0];
            if ((row.header.bytes[0] & M_1011_x4) == 0x90) {
                bool found = true;
                if (subjSet) {
                    if (bytesToInt3(row.bytes) != subjectId.blockId || bytesToInt2(row.bytes + 3) != subjectId.rowId)
                        found = false;
                }
                if (predSet) {
                    if (bytesToInt3(row.bytes + 5) != predicateId.blockId || bytesToInt2(row.bytes + 8) != predicateId.rowId)
                        found = false;
                }
                if (objSet) {
                    if (bytesToInt3(row.bytes + 10) != objectId.blockId || bytesToInt2(row.bytes + 13) != objectId.rowId)
                        found = false;
                }
                if (found) {
                    BufferManager::unpinBlock(bId);
                    return sTripelId{ bId, rId };
                }
            }
            rId++;
        }
        BufferManager::unpinBlock(bId);
        block = NULL;
        bId++;
    }


    throw - 1; //tripel nicht gefunden
}

sTripelId TripelManager::newTripel(sTripel tripel) {
    //TODO: findOrCreate()...
    sSubjectId subjectId;
    sPredicateId predicateId;
    sSubjectId objectId;
    try {
        subjectId = SubjectManager::findSubject(tripel.subject);
    }
    catch (Error) {
        subjectId = SubjectManager::newSubject(tripel.subject);
    }
    try {
        predicateId = PredicateManager::findPredicate(tripel.predicate);
    }
    catch (Error) {
        predicateId = PredicateManager::newPredicate(tripel.predicate);
    }
    try {
        objectId = SubjectManager::findSubject(tripel.object);
    }
    catch (Error) {
        objectId = SubjectManager::newSubject(tripel.object);
    }

    sRow row = claimNewRow(15);
    row.header.bytes[0] = 0x90;

    //subject
    int3ToBytes(row.bytes, subjectId.blockId);
    int2ToBytes(row.bytes + 3, subjectId.rowId);
    //predicate
    int3ToBytes(row.bytes + 5, predicateId.blockId);
    int2ToBytes(row.bytes + 8, predicateId.rowId);
    //object
    int3ToBytes(row.bytes + 10, objectId.blockId);
    int2ToBytes(row.bytes + 13, objectId.rowId);

    //Block ist nicht mehr aktuell
    BufferManager::setModified(row.header.valueId.blockId);
    //Block freigeben
    BufferManager::unpinBlock(row.header.valueId.blockId);

    return row.header.valueId;
}

sTripelId TripelManager::bytesToTripelId(const byte* bytes) {
    return bytesToValueId(bytes);
}